package poker.ai;

import java.util.ArrayList;

import enums.*;
import poker.*;

public class OpponentModeler {

	private Context[] opponentContext;
	private static final int opponentContextSize = 144;
	int tableSize;
	private ArrayList<ArrayList<Context>> playerContext;
	private GameRound gameRound;

	private static OpponentModeler opponentModeler;
	
	private OpponentModeler()
	{
		playerContext = new ArrayList<ArrayList<Context>>();
		for (int i = 0; i < 10; i++) 
		{
			playerContext.add(new ArrayList<Context>());
		}
	}
	
	public void addContext(int playerIndex, Context context)
	{
		playerContext.get(playerIndex).add(context);
	}
	
	public static OpponentModeler getInstance()
	{
		if(opponentModeler == null)
			opponentModeler = new OpponentModeler();
		
		return opponentModeler;
	}
	
	public void setGameRound(GameRound gameRound)
	{
		this.gameRound = gameRound;

		if(gameRound == null)
			this.tableSize = 1;
		else
			this.tableSize = gameRound.getPlayers().size();
		opponentContext = new Context[opponentContextSize*tableSize];
		fillOpponentModeler();
	}
	
	private int getIndex(int playerIndex, State state, PlayersLeft playersLeft, TablePosition tablePosition, ActionEnum action)
	{
		
		int stateBegin = State.getIndex(state) * PlayersLeft.values().length * TablePosition.values().length * ActionEnum.values().length;
		int playersLeftBegin = PlayersLeft.getIndex(playersLeft) * TablePosition.values().length * ActionEnum.values().length;
		int tablePositionBegin = TablePosition.getIndex(tablePosition) * ActionEnum.values().length;
		int actionBegin = ActionEnum.getIndex(action);
		return 144*playerIndex + stateBegin + playersLeftBegin + tablePositionBegin + actionBegin;
	}

	public double getOpponentContextStatistics(int playerIndex, State state, PlayersLeft playersLeft, 
			TablePosition tablePosition, ActionEnum actionEnum)
	{		
		return opponentContext[getIndex(playerIndex, state, playersLeft, tablePosition, actionEnum)].getHandStrength();
	}

	private void setOpponentContextStatistics(int playerIndex, State state, PlayersLeft playersLeft, 
			TablePosition tablePosition, ActionEnum action, double handStrength)
	{
		int statisticsCounter;
		double previousHandStrength;
		double newHandStrength;
		
		int index = getIndex(playerIndex, state, playersLeft, tablePosition, action);
		statisticsCounter = opponentContext[index].getCounter();
		opponentContext[index].setCounter(++statisticsCounter);
		previousHandStrength = opponentContext[index].getHandStrength();
		newHandStrength = ((previousHandStrength + handStrength) / (statisticsCounter));

		opponentContext[index].setHandStrength(newHandStrength);
	}

	private void fillOpponentModeler(){
		int counter = 0;
		
		while(counter < opponentContextSize*tableSize){
			for (int state = 0; state < State.values().length; state++) {
				for (int playersLeft = 0; playersLeft < PlayersLeft.values().length; playersLeft++) {
					for (int tablePosition = 0; tablePosition < TablePosition.values().length; tablePosition++) {
						for (int actions = 0; actions < ActionEnum.values().length; actions++) {
							opponentContext[counter] = 
									new Context(
											State.values()[state], 
											PlayersLeft.values()[playersLeft], 
											0,
											TablePosition.values()[tablePosition], 
											ActionEnum.values()[actions]);
							counter++;
						}
					}
				}
			}
		}
	}
	
	public void testPrint()
	{
		for (int i = 0; i < opponentContext.length; i++) 
		{
			System.out.println(opponentContext[i] + ": " + opponentContext[i].getHandStrength() + " - count: "+ opponentContext[i].getCounter());
		}
	}
	
	public void addOpponentStatistics(Player player) 
	{
		int playerIndex = gameRound.getPosition(player);
		
		for (Context context: playerContext.get(playerIndex)) 
		{
			setOpponentContextStatistics(playerIndex, 
										 context.getStateEnum(), 
									 	 context.getPlayersLeftEnum(), 
									 	 context.getTablePositionEnum(), 
									 	 context.getActionEnum(), 
									 	 getHandStrength(player, context.getStateEnum(), context.getPlayersLeft()));
		}
		
		playerContext.get(playerIndex).clear();
	}
	
	private double getHandStrength(Player player, State state, int playersLeft)
	{
		Card[] cards = gameRound.getHand(player);
		switch(state)
		{
		case preflop:
			return HandStrength.getHandStrengthPreFlop(cards, playersLeft);
		case flop: 
			if(gameRound.getCommunityCards() == null || gameRound.getCommunityCards().length == 0)
			{
				return HandStrength.getHandStrengthPreFlop(cards, playersLeft);
			}
			return HandStrength.calculateHandStrength(player.getPocketCards(), gameRound.getFlop(), playersLeft);
		case river:
			Card[] comm2;
			if(gameRound.getRiverCard() != null && gameRound.getTurnCard() != null)
				comm2 = HandStrength.combine(gameRound.getFlop(), new Card[] { gameRound.getTurnCard(), gameRound.getRiverCard() });
			else if(gameRound.getRiverCard() != null)
				comm2 = HandStrength.combine(gameRound.getFlop(), new Card[] { gameRound.getRiverCard() });
			else if(gameRound.getTurnCard() != null)
				comm2 = HandStrength.combine(gameRound.getFlop(), new Card[] { gameRound.getTurnCard() });
			else
				comm2 = gameRound.getFlop();
			return HandStrength.calculateHandStrength(player.getPocketCards(), comm2, playersLeft);
		case turn:
			Card[] comm;
			if(gameRound.getTurnCard() != null)
			{
				comm = HandStrength.combine(gameRound.getFlop(), new Card[] {gameRound.getTurnCard() } );
			}
			else
				comm = gameRound.getFlop();
			return HandStrength.calculateHandStrength(player.getPocketCards(), comm, playersLeft);
		}
		return -1;
	}
}
